package unidadeControle;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;

import microprocessador.ULA;
import excecao.ExcecaoGeradorEndereco;

public class GeradorEndereco {
	
	private static GeradorEndereco instancia = null;
	private HashMap<String, String[]> mapa;

	private int posicaoMicro;
	
	private GeradorEndereco(){
		this.posicaoMicro = 0;
		this.mapa = new HashMap<String, String[]>();
		
		carregaInstrucoes();
	}
	
	public void reseta(){
		this.posicaoMicro = 0;
	}
	
	public static void criaInstancia(){
		if (instancia == null){
			instancia = new GeradorEndereco();
		}
	}
	
	public static GeradorEndereco getInstancia(){
		return instancia;
	}	
	
	public int getPosicaoMicro(){
		return this.posicaoMicro;
	}	
	
	public void incPosicaoMicro(String codInstrucao) throws ExcecaoGeradorEndereco{
		if (mapa.containsKey(codInstrucao)){
			String[] micros = mapa.get(codInstrucao);
			
			this.posicaoMicro += 1;
			
			if (this.posicaoMicro == micros.length){
				this.posicaoMicro = 0;
			}
		}
		else{
			throw new ExcecaoGeradorEndereco("Instrucao " + codInstrucao + " não foi reconhecida");
		}
	}	
	
	private void carregaInstrucoes(){
	    try {
	        BufferedReader in = new BufferedReader(new FileReader("geradorEndereco.txt"));
            String str;
            while (in.ready()) {
                str = in.readLine();
                String[] i = str.split(":");
                String[] v = i[1].split(" ");                
                mapa.put(i[0], v);
            }
            in.close();
	    }
	    catch (IOException e) {
	    	System.out.println(e.getMessage());
	    }
	}
	
	// Retorna o endereco da memoria de controle que contem a proxima microinstrucao
	public int getEnderecoMicroInstrucao(String flags, String codInstrucao, String endMicroInstrucao) throws ExcecaoGeradorEndereco{
		
		if (mapa.containsKey(codInstrucao)){
			String[] micros = mapa.get(codInstrucao);
			
			if (codInstrucao.equals("11111111111111111111")){
				return -1;
			}
			
			if (this.posicaoMicro < micros.length){
				int pos = Integer.parseInt(micros[this.posicaoMicro]);
				
				// BRC
				if (codInstrucao.equals("00000000000011001001") && this.posicaoMicro == 3 && flags.charAt(ULA.C) == '1'){
					return 16;
				}
				
				// BRE
				else if (codInstrucao.equals("00000000001001001111") && this.posicaoMicro == 3 && flags.charAt(ULA.Z) == '1'){
					return 16;
				}					
					
				// BRG
				else if (codInstrucao.equals("00000000001010000100") && this.posicaoMicro == 3 && (flags.charAt(ULA.N) == '1' && flags.charAt(ULA.Z) == '0')){
					return 16;
				}
				
				// BRL
				else if (codInstrucao.equals("00000000000110000011") && this.posicaoMicro == 3 && (flags.charAt(ULA.N) == '0' && flags.charAt(ULA.Z) == '0')){
					return 16;
				}
					
				// BRN
				else if (codInstrucao.equals("00000000001001001110") && this.posicaoMicro == 3 && flags.charAt(ULA.N) == '1'){
					return 16;
				}
				
				// BRZ
				else if (codInstrucao.equals("00000000000011000000") && this.posicaoMicro == 3 && flags.charAt(ULA.Z) == '0'){
					return 16;
				}
				
				// JP
				else if (codInstrucao.equals("00000000000001110110")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.P) == '1')){
						return 16;					
					}
				}	
				
				// JPE
				else if (codInstrucao.equals("00000000000000010000")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.P) == '1')){
						return 16;					
					}
				}
				
				// JPO
				else if (codInstrucao.equals("00000000000100011110")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.P) == '0')){
						return 16;					
					}
				}				
				
				// JO
				else if (codInstrucao.equals("00000000001001100010")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.O) == '1')){
						return 16;					
					}
				}	
				
				// JNO
				else if (codInstrucao.equals("00000000001100011010")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.O) == '0')){
						return 16;					
					}
				}				
				
				// JS
				else if (codInstrucao.equals("00000000001100110110")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.N) == '1')){
						return 16;					
					}
				}	
				
				// JNS
				else if (codInstrucao.equals("00000000001001101111")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.N) == '0')){
						return 16;					
					}
				}				
				
				// JG JNLE
				else if (codInstrucao.equals("00000000000110110001") || codInstrucao.equals("00000000000110001100")){
					if (this.posicaoMicro == 3 && ((flags.charAt(ULA.N) == flags.charAt(ULA.O)) && (flags.charAt(ULA.Z) == '0'))){
						return 16;					
					}
				}				
				
				// JNG JLE
				else if (codInstrucao.equals("00000000001000011010") || codInstrucao.equals("00000000000111000100")){
					if (this.posicaoMicro == 3 && ((flags.charAt(ULA.N) != flags.charAt(ULA.O)) || (flags.charAt(ULA.Z) == '1'))){
						return 16;					
					}
				}				
				
				// JGE JNL
				else if (codInstrucao.equals("00000000000110101000") || codInstrucao.equals("00000000001000111000")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.N) == (flags.charAt(ULA.O)))){
						return 16;					
					}
				}				
				
				// JL JNGE
				else if (codInstrucao.equals("00000000001011111000") || codInstrucao.equals("00000000001001010101")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.N) != (flags.charAt(ULA.O)))){
						return 16;					
					}
				}					
				
				// JNE JNZ
				else if (codInstrucao.equals("00000000000101101001") || codInstrucao.equals("00000000000100000011")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.Z) == '0')){
						return 16;					
					}
				}				
				
				// JE JZ
				else if (codInstrucao.equals("00000000001010111011") || codInstrucao.equals("00000000000111000001")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.Z) == '1')){
						return 16;					
					}
				}				
				
				// JB JNAE JC
				else if (codInstrucao.equals("00000000000010100111") || codInstrucao.equals("00000000001000101001") || codInstrucao.equals("00000000001100111111")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.C) == '1')){
						return 16;					
					}
				}				
				
				// JAE JNB JNC
				else if (codInstrucao.equals("00000000001010111000") || codInstrucao.equals("00000000001010110110") || codInstrucao.equals("00000000001100111011")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.C) == '0')){
						return 16;					
					}
				}				
				
				// JNA JBE
				else if (codInstrucao.equals("00000000000000001010") || codInstrucao.equals("00000000000111101011")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.C) == '1' || flags.charAt(ULA.Z) == '1')){
						return 16;					
					}
				}
				
				// JA JNBE
				else if (codInstrucao.equals("00000000000111101000") || codInstrucao.equals("00000000000111101000")){
					if (this.posicaoMicro == 3 && (flags.charAt(ULA.C) == '0' && flags.charAt(ULA.Z) == '0')){
						return 16;					
					}
				}				
				
				// LOOP
				else if (codInstrucao.equals("00000000001001100110") && this.posicaoMicro == 4 && flags.charAt(ULA.Z) == '0'){
					return 16;
				}				
				
				return pos;
			}
		}
		
		throw new ExcecaoGeradorEndereco("Instrucao " + codInstrucao + " não foi reconhecida");
	}
}
